#include "net.h"
#include "ip.h"
#include "ethernet.h"
#include "arp.h"
#include "icmp.h"
#include<string.h>

/**
 * @brief ip标识
*/
uint16_t ip_id = 0;

/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_mac 源mac地址
 */
void ip_in(buf_t *buf, uint8_t *src_mac)
{
    if (buf->len < sizeof(ip_hdr_t))
        return ;
    
    ip_hdr_t *hdr = (ip_hdr_t *)buf->data;
    if (hdr->version != IP_VERSION_4 || 
        swap16(hdr->total_len16) > buf->len || 
        memcmp(net_if_ip, hdr->dst_ip, NET_IP_LEN) ||
        hdr->ttl == 0)
        return ;

    // 网卡驱动自动计算校验和，无需协议栈进行计算
#ifndef Ping
    if (checksum16((uint16_t *)buf->data, sizeof(ip_hdr_t))) //校验和(包括CRC字段后)应为0xFFFF,取反为0
        return ;
#endif

    if (!net_protocol_exist(hdr->protocol)){
        icmp_unreachable(buf, hdr->src_ip, ICMP_CODE_PROTOCOL_UNREACH);
        return ;
    }

    buf_remove_padding(buf, buf->len - swap16(hdr->total_len16));
    buf_remove_header(buf, sizeof(ip_hdr_t));
    if (net_in(buf, hdr->protocol, hdr->src_ip) == -1){
        fprintf(stderr, "ip_in failed\n");
    }
}

/**
 * @brief 处理一个要发送的ip分片
 * 
 * @param buf 要发送的分片
 * @param ip 目标ip地址
 * @param protocol 上层协议
 * @param id 数据包id
 * @param offset 分片offset，必须被8整除
 * @param mf 分片mf标志，是否有下一个分片
 */
void ip_fragment_out(buf_t *buf, uint8_t *ip, net_protocol_t protocol, int id, uint16_t offset, int mf)
{
    uint16_t len = buf->len + sizeof(ip_hdr_t);
    uint16_t flags_fragment = (mf<<13) | offset;
    ip_hdr_t hdr = {
        .hdr_len = sizeof(ip_hdr_t) / IP_HDR_LEN_PER_BYTE,        
        .version = IP_VERSION_4,       
        .tos = 0,              
        .total_len16 = swap16(len),
        .id16 = swap16(id),
        .flags_fragment16 = swap16(flags_fragment),
        .ttl = IP_DEFALUT_TTL,                
        .protocol = protocol,
        .hdr_checksum16 = 0,
        .src_ip = NET_IF_IP
    };
    memcpy(hdr.dst_ip, ip, NET_IP_LEN);
    hdr.hdr_checksum16 = checksum16((uint16_t *)&hdr, sizeof(ip_hdr_t)); //头部已经是大端序了，求出的校验和就是大端序，不需要转换大小端

    buf_add_header(buf, sizeof(ip_hdr_t));
    memcpy(buf->data, &hdr, sizeof(ip_hdr_t));

    arp_out(buf, ip);
}

/**
 * @brief 处理一个要发送的ip数据包
 * 
 * @param buf 要处理的包
 * @param ip 目标ip地址
 * @param protocol 上层协议
 */
void ip_out(buf_t *buf, uint8_t *ip, net_protocol_t protocol)
{
    uint16_t offset = 0;
    size_t len = buf->len;
    buf_t *tbuf = (buf_t *)malloc(sizeof(buf_t));

    while (len - offset * IP_HDR_OFFSET_PER_BYTE > ETHERNET_MAX_TRANSPORT_UNIT){
        size_t l = ETHERNET_MAX_TRANSPORT_UNIT - sizeof(ip_hdr_t);
        buf_init(tbuf, l);
        memcpy(tbuf->data, buf->data + offset * IP_HDR_OFFSET_PER_BYTE, l);
        ip_fragment_out(tbuf, ip, protocol, ip_id, offset, 1);
        offset += (ETHERNET_MAX_TRANSPORT_UNIT - sizeof(ip_hdr_t)) / IP_HDR_OFFSET_PER_BYTE;
    } 

    size_t l = len - offset * IP_HDR_OFFSET_PER_BYTE;
    buf_init(tbuf, l);
    memcpy(tbuf->data, buf->data + offset * IP_HDR_OFFSET_PER_BYTE, l);
    ip_fragment_out(tbuf, ip, protocol, ip_id++, offset, 0);

    free(tbuf);
}

/**
 * @brief 初始化ip协议
 * 
 */
void ip_init()
{
    net_add_protocol(NET_PROTOCOL_IP, ip_in);
}